import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
import 'package:openim_common/openim_common.dart';
import 'package:rxdart/rxdart.dart';
import 'package:uuid/uuid.dart';

import '../openim_live.dart';

mixin OpenIMLive {
  final signalingSubject = PublishSubject<CallEvent>();

  void invitationCancelled(SignalingInfo info) {
    signalingSubject.add(CallEvent(CallState.beCanceled, info));
  }

  void inviteeAccepted(SignalingInfo info) {
    signalingSubject.add(CallEvent(CallState.beAccepted, info));
  }

  void inviteeRejected(SignalingInfo info) {
    signalingSubject.add(CallEvent(CallState.beRejected, info));
  }

  void receiveNewInvitation(SignalingInfo info) {
    // 检查通话邀请是否已过期（假设超时时间为30秒）
    final invitationTime = info.invitation?.timeout ?? 30;
    final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
    
    // 如果消息是离线消息，检查是否已过期
    if (info.invitation != null) {
      // 这里可以根据实际需求添加过期逻辑
      // 例如：如果消息创建时间 + 超时时间 < 当前时间，则忽略
    }
    
    // 加入好友校验，陌生人通话邀请直接拦截
    final String? inviterID = info.invitation?.inviterUserID ?? info.userID;
    if (inviterID == null) {
      Logger.print('通话邀请缺少邀请者ID，忽略处理');
      return;
    }
    
    OpenIM.iMManager.friendshipManager
        .getFriendsInfo(userIDList: [inviterID])
        .then((friendsInfo) async {
      if (friendsInfo.isEmpty) {
        Logger.print('拦截陌生人通话邀请，拒绝处理: $inviterID');
    
        // 可选：自动回复“无法通话”的提示，保持与其他拦截一致
        try {
          final notificationMessage = await OpenIM
              .iMManager
              .messageManager
              .createCustomMessage(
            data: json.encode({
              'customType': CustomMessageType.deletedByFriend,
              'data': {
                'tips': '对方不是您的好友，无法通话',
                'isAutoReply': true,
              }
            }),
            extension: '',
            description: '通话拦截提醒',
          );
    
          await OpenIM.iMManager.messageManager.sendMessage(
            message: notificationMessage,
            userID: inviterID,
            groupID: null,
            offlinePushInfo: OfflinePushInfo(
              title: '系统通知',
              desc: '对方不是您的好友，无法通话',
            ),
          );
        } catch (e) {
          Logger.print('发送通话拦截自动回复失败: $e');
        }
    
        return; // 非好友直接终止处理
      }
    
    // 是好友才投递来电事件
    signalingSubject.add(CallEvent(CallState.beCalled, info));
    }).catchError((e) {
    // 校验失败时，稳妥起见也拦截处理
    Logger.print('好友校验失败，拦截通话邀请: $e');
    });
  }

  void beHangup(SignalingInfo info) {
    signalingSubject.add(CallEvent(CallState.beHangup, info));
  }

  final backgroundSubject = PublishSubject<bool>();

  final insertSignalingMessageSubject = PublishSubject<CallEvent>();

  Function(SignalingMessageEvent)? onSignalingMessage;
  final roomParticipantDisconnectedSubject = PublishSubject<RoomCallingInfo>();
  final roomParticipantConnectedSubject = PublishSubject<RoomCallingInfo>();

  bool _isRunningBackground = false;

  CallEvent? _beCalledEvent;

  bool _autoPickup = false;
  
  // 保存当前通话消息的ID，用于后续更新
  String? _currentCallMessageID;
  Map<String, dynamic>? _currentCallInfo;

  final _ring = 'assets/audio/live_ring.wav';
  final _audioPlayer = AudioPlayer(
    // Handle audio_session events ourselves for the purpose of this demo.
    handleInterruptions: false,
    // androidApplyAudioAttributes: false,
    // handleAudioSessionActivation: false,
  );

  bool get isBusy => OpenIMLiveClient().isBusy;

  onCloseLive() {
    signalingSubject.close();
    backgroundSubject.close();
    roomParticipantDisconnectedSubject.close();
    roomParticipantConnectedSubject.close();
    _stopSound();
  }

  onInitLive() async {
    _signalingListener();
    _insertSignalingMessageListener();
    backgroundSubject.listen((background) {
      _isRunningBackground = background;
      if (!_isRunningBackground) {
        if (_beCalledEvent != null) {
          signalingSubject.add(_beCalledEvent!);
        }
      }
    });

    roomParticipantDisconnectedSubject.listen((info) {
      if (null == info.participant || info.participant!.length == 1) {
        OpenIMLiveClient().closeByRoomID(info.invitation!.roomID!);
      }
    });
  }

  Stream<CallEvent> get _stream => signalingSubject.stream /*.where((event) => LiveClient.dispatchSignaling(event))*/;

  _signalingListener() => _stream.listen(
        (event) async {
          _beCalledEvent = null;
          if (event.state == CallState.beCalled) {
            _playSound();
            final mediaType = event.data.invitation!.mediaType;
            final sessionType = event.data.invitation!.sessionType;
            final callType = mediaType == 'audio' ? CallType.audio : CallType.video;
            final callObj = sessionType == ConversationType.single ? CallObj.single : CallObj.group;

            if (Platform.isAndroid && _isRunningBackground) {
              _beCalledEvent = event;
              if (await Permissions.checkSystemAlertWindow()) {
                return;
              }
            }
            _beCalledEvent = null;
            OpenIMLiveClient().start(
              Get.overlayContext!,
              callEventSubject: signalingSubject,
              roomID: event.data.invitation!.roomID!,
              inviteeUserIDList: event.data.invitation!.inviteeUserIDList!,
              inviterUserID: event.data.invitation!.inviterUserID!,
              groupID: event.data.invitation!.groupID,
              callType: callType,
              callObj: callObj,
              initState: CallState.beCalled,
              onSyncUserInfo: onSyncUserInfo,
              onSyncGroupInfo: onSyncGroupInfo,
              onSyncGroupMemberInfo: onSyncGroupMemberInfo,
              autoPickup: _autoPickup,
              onTapPickup: () => onTapPickup(
                event.data..userID = OpenIM.iMManager.userID,
              ),
              onTapReject: () => onTapReject(
                event.data..userID = OpenIM.iMManager.userID,
              ),
              onTapHangup: (duration, isPositive) => onTapHangup(
                event.data..userID = OpenIM.iMManager.userID,
                duration,
                isPositive,
              ),
              onError: onError,
              onRoomDisconnected: () => onRoomDisconnected(event.data),
            );
          } else if (event.state == CallState.beRejected) {
            insertSignalingMessageSubject.add(event);
            _stopSound();
          } else if (event.state == CallState.beHangup) {
            _stopSound();
          } else if (event.state == CallState.beCanceled) {
            insertSignalingMessageSubject.add(event);
            _stopSound();
          } else if (event.state == CallState.beAccepted) {
            _stopSound();
          } else if (event.state == CallState.otherReject || event.state == CallState.otherAccepted) {
            _stopSound();
          } else if (event.state == CallState.timeout) {
            insertSignalingMessageSubject.add(event);

            _stopSound();
            final sessionType = event.data.invitation!.sessionType;

            if (sessionType == 1) {
              onTimeoutCancelled(event.data);
            }
          }
        },
      );

  _insertSignalingMessageListener() {
    insertSignalingMessageSubject.listen((value) {
      _insertMessage(
        state: value.state,
        signalingInfo: value.data,
        duration: value.fields ?? 0,
      );
    });
  }

  call({
    required CallObj callObj,
    required CallType callType,
    CallState callState = CallState.call,
    String? roomID,
    String? inviterUserID,
    required List<String> inviteeUserIDList,
    String? groupID,
    SignalingCertificate? credentials,
  }) async {
    final mediaType = callType == CallType.audio ? 'audio' : 'video';
    final sessionType = callObj == CallObj.single ? 1 : 3;
    inviterUserID ??= OpenIM.iMManager.userID;

    final signal = SignalingInfo(
      userID: inviterUserID,
      invitation: InvitationInfo(
        inviterUserID: inviterUserID,
        inviteeUserIDList: inviteeUserIDList,
        roomID: roomID ?? groupID ?? const Uuid().v4(),
        timeout: 30,
        mediaType: mediaType,
        sessionType: sessionType,
        platformID: IMUtils.getPlatform(),
        groupID: groupID,
      ),
    );

    OpenIMLiveClient().start(
      Get.overlayContext!,
      callEventSubject: signalingSubject,
      inviterUserID: inviterUserID,
      groupID: groupID,
      inviteeUserIDList: inviteeUserIDList,
      callObj: callObj,
      callType: callType,
      initState: callState,
      onDialSingle: () => onDialSingle(signal),
      onJoinGroup: () => Future.value(credentials!),
      onTapCancel: () => onTapCancel(signal),
      onTapHangup: (duration, isPositive) => onTapHangup(
        signal,
        duration,
        isPositive,
      ),
      onSyncUserInfo: onSyncUserInfo,
      onSyncGroupInfo: onSyncGroupInfo,
      onSyncGroupMemberInfo: onSyncGroupMemberInfo,
      onWaitingAccept: () {
        if (callObj == CallObj.single) _playSound();
      },
      onBusyLine: onBusyLine,
      onStartCalling: () {
        _stopSound();
      },
      onError: onError,
      onRoomDisconnected: () => onRoomDisconnected(signal),
      onClose: _stopSound,
    );
  }

  onError(error, stack) {
    Logger.print('onError=====> $error $stack');
    OpenIMLiveClient().close();
    _stopSound();
    if (error is PlatformException) {
      if (int.parse(error.code) == SDKErrorCode.hasBeenBlocked) {
        IMViews.showToast(StrRes.callFail);
        return;
      }
    }
    IMViews.showToast(StrRes.networkError);
  }

  onRoomDisconnected(SignalingInfo signalingInfo) {}

  Future<SignalingCertificate> onDialSingle(SignalingInfo signaling) async {
    // 不再立即创建通话记录消息，只在通话结束时创建最终结果消息
    // 保存通话信息，用于后续创建消息
    _currentCallInfo = {
      'inviterUserID': OpenIM.iMManager.userID,
      'inviteeUserID': signaling.invitation!.inviteeUserIDList!.first,
      'mediaType': signaling.invitation!.mediaType!,
      'startTime': DateTime.now().millisecondsSinceEpoch,
    };
    
    // 发送信令消息（不显示在聊天界面）
    final data = {'customType': CustomMessageType.callingInvite, 'data': signaling.invitation!.toJson()};
    final signalingMessage = await OpenIM.iMManager.messageManager
        .createCustomMessage(data: jsonEncode(data), extension: '', description: '');
    
    // 配置离线推送信息
    final offlinePushInfo = OfflinePushInfo(
      title: '语音/视频通话',
      desc: signaling.invitation!.mediaType == 'audio' ? '语音通话邀请' : '视频通话邀请',
      iOSBadgeCount: true,
    );
    
    OpenIM.iMManager.messageManager.sendMessage(
        message: signalingMessage,
        offlinePushInfo: offlinePushInfo,
        userID: signaling.invitation!.inviteeUserIDList!.first,
        isOnlineOnly: true); // 设为 true，信令消息不存储到聊天记录
    
    final certificate = await Apis.getTokenForRTC(signaling.invitation!.roomID!, OpenIM.iMManager.userID);

    return certificate;
  }

  Future<SignalingCertificate> onTapPickup(SignalingInfo signaling) async {
    _beCalledEvent = null; // ios bug
    _autoPickup = false;
    _stopSound();
    final data = {'customType': CustomMessageType.callingAccept, 'data': signaling.invitation!.toJson()};
    final message = await OpenIM.iMManager.messageManager
        .createCustomMessage(data: jsonEncode(data), extension: '', description: '');
    
    // 配置离线推送信息
    final offlinePushInfo = OfflinePushInfo(
      title: '通话已接听',
      desc: '对方已接听通话',
      iOSBadgeCount: true,
    );
    
    OpenIM.iMManager.messageManager.sendMessage(
        message: message,
        offlinePushInfo: offlinePushInfo,
        userID: signaling.invitation!.inviterUserID,
        isOnlineOnly: true); // 设为 true，信令消息不存储到聊天记录
    final certificate = await Apis.getTokenForRTC(signaling.invitation!.roomID!, OpenIM.iMManager.userID);

    return certificate;
  }

  onTapReject(SignalingInfo signaling) async {
    final data = {'customType': CustomMessageType.callingReject, 'data': signaling.invitation!.toJson()};
    final message = await OpenIM.iMManager.messageManager
        .createCustomMessage(data: jsonEncode(data), extension: '', description: '');
    
    // 配置离线推送信息
    final offlinePushInfo = OfflinePushInfo(
      title: '通话已拒绝',
      desc: '通话已被拒绝',
      iOSBadgeCount: true,
    );
    
    OpenIM.iMManager.messageManager.sendMessage(
        message: message,
        offlinePushInfo: offlinePushInfo,
        userID: signaling.invitation!.inviterUserID,
        isOnlineOnly: true); // 设为 true，信令消息不存储到聊天记录
    _stopSound();

    insertSignalingMessageSubject.add(CallEvent(
      CallState.reject,
      signaling,
    ));
  }

  onTapCancel(SignalingInfo signaling) async {
    final data = {'customType': CustomMessageType.callingCancel, 'data': signaling.invitation!.toJson()};
    final message = await OpenIM.iMManager.messageManager
        .createCustomMessage(data: jsonEncode(data), extension: '', description: '');
    final recvUserID = signaling.invitation!.inviterUserID == OpenIM.iMManager.userID
        ? signaling.invitation!.inviteeUserIDList!.first
        : signaling.invitation!.inviterUserID;
    
    // 配置离线推送信息
    final offlinePushInfo = OfflinePushInfo(
      title: '通话已取消',
      desc: '通话已被取消',
      iOSBadgeCount: true,
    );
    
    OpenIM.iMManager.messageManager
        .sendMessage(message: message, offlinePushInfo: offlinePushInfo, userID: recvUserID, isOnlineOnly: true);
    _stopSound();

    insertSignalingMessageSubject.add(CallEvent(
      CallState.cancel,
      signaling,
    ));
  }

  onTimeoutCancelled(SignalingInfo signaling) async {
    final data = {'customType': CustomMessageType.callingCancel, 'data': signaling.invitation!.toJson()};
    final message = await OpenIM.iMManager.messageManager
        .createCustomMessage(data: jsonEncode(data), extension: '', description: '');

    // 配置离线推送信息
    final offlinePushInfo = OfflinePushInfo(
      title: '通话超时',
      desc: '通话邀请已超时',
      iOSBadgeCount: true,
    );

    OpenIM.iMManager.messageManager.sendMessage(
        message: message,
        offlinePushInfo: offlinePushInfo,
        userID: signaling.invitation!.inviterUserID,
        isOnlineOnly: true);

    return true;
  }

  onTapHangup(SignalingInfo signaling, int duration, bool isPositive) async {
    if (isPositive) {
      final data = {'customType': CustomMessageType.callingHungup, 'data': signaling.invitation!.toJson()};
      final message = await OpenIM.iMManager.messageManager
          .createCustomMessage(data: jsonEncode(data), extension: '', description: '');
      final recvUserID = signaling.invitation!.inviterUserID == OpenIM.iMManager.userID
          ? signaling.invitation!.inviteeUserIDList!.first
          : signaling.invitation!.inviterUserID;
      
      // 配置离线推送信息
      final offlinePushInfo = OfflinePushInfo(
        title: '通话已结束',
        desc: '通话时长: ${_formatDuration(duration)}',
        iOSBadgeCount: true,
      );
      
      OpenIM.iMManager.messageManager
          .sendMessage(message: message, offlinePushInfo: offlinePushInfo, userID: recvUserID, isOnlineOnly: true);
    }
    _stopSound();

    insertSignalingMessageSubject.add(CallEvent(
      CallState.hangup,
      signaling,
      fields: duration,
    ));
  }

  // 添加时长格式化方法
  String _formatDuration(int seconds) {
    final minutes = seconds ~/ 60;
    final remainingSeconds = seconds % 60;
    return '${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
  }

  // // 在 receiveNewInvitation 方法中添加过期检查
  // void receiveNewInvitation(SignalingInfo info) {
  //   // 检查通话邀请是否已过期（假设超时时间为30秒）
  //   final invitationTime = info.invitation?.timeout ?? 30;
  //   final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
    
  //   // 如果消息是离线消息，检查是否已过期
  //   if (info.invitation != null) {
  //     // 这里可以根据实际需求添加过期逻辑
  //     // 例如：如果消息创建时间 + 超时时间 < 当前时间，则忽略
  //   }
    
  //   signalingSubject.add(CallEvent(CallState.beCalled, info));
  // }

  onBusyLine() {
    _stopSound();
    IMViews.showToast(StrRes.busyVideoCallHint);
  }

  onJoin() {}

  Future<UserInfo?> onSyncUserInfo(userID) async {
    var list = await OpenIM.iMManager.userManager.getUsersInfo(
      userIDList: [userID],
    );

    return list.firstOrNull?.simpleUserInfo;
  }

  Future<GroupInfo?> onSyncGroupInfo(groupID) async {
    var list = await OpenIM.iMManager.groupManager.getGroupsInfo(
      groupIDList: [groupID],
    );
    return list.firstOrNull;
  }

  Future<List<GroupMembersInfo>> onSyncGroupMemberInfo(groupID, userIDList) async {
    var list = await OpenIM.iMManager.groupManager.getGroupMembersInfo(
      groupID: groupID,
      userIDList: userIDList,
    );
    return list;
  }

  void _playSound() async {
    if (!_audioPlayer.playerState.playing) {
      _audioPlayer.setAsset(_ring, package: 'openim_common');
      _audioPlayer.setLoopMode(LoopMode.one);
      _audioPlayer.setVolume(1.0);
      _audioPlayer.play();
    }
  }

  void _stopSound() async {
    if (_audioPlayer.playerState.playing) {
      _audioPlayer.stop();
    }
  }

  void _insertMessage({
    required CallState state,
    required SignalingInfo signalingInfo,
    int duration = 0,
  }) async {
    (() async {
      var invitation = signalingInfo.invitation;
      var mediaType = invitation!.mediaType;
      var inviterUserID = invitation.inviterUserID;
      var inviteeUserID = invitation.inviteeUserIDList!.first;
      var groupID = invitation.groupID;
      Logger.print(
          'end calling and insert message state:${state.name}, mediaType:$mediaType, inviterUserID:$inviterUserID, inviteeUserID:$inviteeUserID, groupID:$groupID, duration:$duration',
          functionName: '_insertMessage');

      // 只在通话真正结束时才创建通话记录（包括hangup、cancel、beCanceled、reject、beRejected、timeout等状态）
      if (state != CallState.hangup && 
          state != CallState.cancel && 
          state != CallState.beCanceled && 
          state != CallState.reject && 
          state != CallState.beRejected && 
          state != CallState.timeout) {
        return;
      }

      // 确定最终的通话参数
      String finalInviterUserID;
      String finalInviteeUserID;
      String finalMediaType;
      
      // 优先使用保存的通话信息（主叫方场景）
      if (_currentCallInfo != null) {
        finalInviterUserID = _currentCallInfo!['inviterUserID'];
        finalInviteeUserID = _currentCallInfo!['inviteeUserID'];
        finalMediaType = _currentCallInfo!['mediaType'];
        // 清除保存的通话信息
        _currentCallInfo = null;
      } else {
        // 如果没有保存的信息，使用信令信息中的参数（被叫方场景或兼容旧逻辑）
        finalInviterUserID = inviterUserID ?? '';  // 处理可空类型
        finalInviteeUserID = inviteeUserID ?? '';  // 处理可空类型
        finalMediaType = mediaType ?? 'video';     // 处理可空类型，默认为video
      }
      
      // 创建最终的通话记录消息
      final callMessage = await OpenIM.iMManager.messageManager.createCallMessage(
        state: state.name,
        type: finalMediaType,
        duration: duration,
      );
      
      String? receiverID;
      if (finalInviterUserID != OpenIM.iMManager.userID) {
        receiverID = finalInviterUserID;
      } else {
        receiverID = finalInviteeUserID;
      }

      var msg = await OpenIM.iMManager.messageManager.insertSingleMessageToLocalStorage(
        receiverID: finalInviteeUserID,
        senderID: finalInviterUserID,
        message: callMessage
          ..status = 2
          ..isRead = true,
      );

      onSignalingMessage?.call(SignalingMessageEvent(msg, 1, receiverID, null));
    })();
  }
}

class SignalingMessageEvent {
  Message message;
  String? userID;
  String? groupID;
  int sessionType;

  SignalingMessageEvent(
    this.message,
    this.sessionType,
    this.userID,
    this.groupID,
  );

  bool get isSingleChat => sessionType == ConversationType.single;

  bool get isGroupChat => sessionType == ConversationType.group || sessionType == ConversationType.superGroup;
}

extension MessageMangerExt on MessageManager {
  Future<Message> createCallMessage({
    required String type,
    required String state,
    int? duration,
  }) =>
      createCustomMessage(
        data: json.encode({
          "customType": CustomMessageType.call,
          "data": {
            'duration': duration,
            'state': state,
            'type': type,
          },
        }),
        extension: '',
        description: '',
      );
}
